package query

import (
	"path/filepath"
	"strings"

	"github.com/glennliao/apijson-go/consts"
	"github.com/glennliao/apijson-go/model"
)

type J结构节点 struct {
	J节点 *J节点
}

func X新建结构节点(n *J节点) *J结构节点 {
	return &J结构节点{J节点: n}
}

func (h *J结构节点) J解析() {
	当前节点 := h.J节点

	for _, 子节点 := range 当前节点.Z子节点 {
		子节点.J解析()
	}

	if 当前节点.S是列表 { // []节点
		存在主表 := false // 是否存在主查询表
		for _, 子节点 := range 当前节点.Z子节点 {

			if 子节点.C错误 != nil {
				当前节点.C错误 = 子节点.C错误
				return
			}

			if 子节点.Z主表键 != "" {

				if 存在主表 {
					当前节点.C错误 = consts.X新建请求验证错误("节点必须只有一个主表: " + 当前节点.L路径)
					return
				}

				存在主表 = true
				当前节点.Z主表键 = filepath.Base(子节点.J键)
				子节点.F分页参数 = 当前节点.F分页参数

			}
		}

		if 当前节点.J键 == consts.L列表键后缀 && !存在主表 {
			当前节点.C错误 = consts.X新建请求验证错误("节点必须包含主表: " + 当前节点.L路径)
			return
		}
	}
}

func (h *J结构节点) H获取数据() {
	当前节点 := h.J节点
	// 目前结构节点组装数据在result, 如果被依赖的是组装后的, 则无法查询。如遇到此情况再看
	if 当前节点.S是列表 && 当前节点.X需要总数 {
		当前节点.Z总数 = 当前节点.Z子节点[当前节点.Z主表键].Z总数
	}
}

func (h *J结构节点) C处理结果() {
	当前节点 := h.J节点
	if 当前节点.S是列表 {
		var 结果列表 []model.Map

		var 主表列表 []model.Map

		if 当前节点.Z子节点[当前节点.Z主表键].F返回值 != nil {
			主表列表 = 当前节点.Z子节点[当前节点.Z主表键].F返回值.([]model.Map)

			for i := 0; i < len(主表列表); i++ {

				主表项 := 主表列表[i]

				项 := model.Map{
					当前节点.Z主表键: 主表项,
				}

				// 遍历组装数据,后续考虑使用别的方案优化(暂未简单使用map的id->item,主要考虑多字段问题)
				for 子键, 子节点 := range 当前节点.Z子节点 {
					if 子节点.Z主表键 == "" {
						if 子节点.F返回值 != nil {

							var 子结果列表 []model.Map

							for _, 依赖结果项 := range 子节点.F返回值.([]model.Map) {
								匹配 := true
								for 引用键, 引用节点 := range 子节点.Y引用键映射 {
									if 主表项[引用节点.Z字段] != 依赖结果项[引用键] {
										匹配 = false
										break
									}
								}
								if 匹配 {
									子结果列表 = append(子结果列表, 依赖结果项)
								}

							}
							if len(子结果列表) > 0 {
								if strings.HasSuffix(子键, consts.L列表键后缀) {
									项[子键] = 子结果列表
								} else {
									项[子键] = 子结果列表[0]
								}
							}

						}

					}
				}

				结果列表 = append(结果列表, 项)
			}
		}

		当前节点.F返回值 = 结果列表

		if len(当前节点.F返回值.([]model.Map)) == 0 {
			当前节点.F返回值 = []model.Map{}
		}
	} else {

		结果映射 := model.Map{}
		for 键, 节点 := range 当前节点.Z子节点 {
			var 错误 error

			if strings.HasSuffix(键, consts.Y引用键后缀) {
				键 = 键[0 : len(键)-1]
			}
			if strings.HasSuffix(键, consts.H函数原始请求参数) {
				键 = 键[0 : len(键)-2]
			}

			// 增加alias用来重命名返回的key，避免前端调整取值
			if 节点.Q请求数据[consts.B别名] != nil {
				键 = 节点.Q请求数据[consts.B别名].(string)
			}

			结果映射[键], 错误 = 节点.J结果()
			if 节点.L类型 == J节点类型函数 && 结果映射[键] == nil {
				delete(结果映射, 键)
			}

			if 错误 != nil {
				当前节点.C错误 = 错误
			}
		}

		if len(结果映射) > 0 && 当前节点.J简单请求值 == "" {
			当前节点.F返回值 = 结果映射
		} else {
			当前节点.F返回值 = 当前节点.J简单请求值
		}

	}
}

func (h *J结构节点) J节点类型() int {
	return J节点类型结构
}
